package com.atguigu.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.client.ProductFeignClient;
import com.atguigu.dao.ProductMapper;
import com.atguigu.entity.*;
import com.atguigu.search.*;
import com.atguigu.service.SearchService;
import lombok.SneakyThrows;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private ProductFeignClient productFeignClient;
    @Autowired
    private ProductMapper productMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Override
    public void onSale(Long skuId) {

        Product product = new Product();

        //a.商品的基本信息
        SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
        if (skuInfo != null) {
            product.setId(skuInfo.getId());
            product.setProductName(skuInfo.getSkuName());
            product.setCreateTime(new Date());
            product.setDefaultImage(skuInfo.getSkuDefaultImg());
            product.setPrice(skuInfo.getPrice().doubleValue());
        }
        //b.品牌的信息
        BaseBrand brand = productFeignClient.getBrandById(skuInfo.getBrandId());
        if (brand != null) {
            product.setBrandId(brand.getId());
            product.setBrandName(brand.getBrandName());
            product.setBrandLogoUrl(brand.getBrandLogoUrl());
        }
        //c.商品的分类信息
        BaseCategoryView categoryView = productFeignClient.getCategoryView(skuInfo.getCategory3Id());
        if (categoryView != null) {
            product.setCategory1Id(categoryView.getCategory1Id());
            product.setCategory1Name(categoryView.getCategory1Name());
            product.setCategory2Id(categoryView.getCategory2Id());
            product.setCategory2Name(categoryView.getCategory2Name());
            product.setCategory3Id(categoryView.getCategory3Id());
            product.setCategory3Name(categoryView.getCategory3Name());
        }
        //d.根据skuId查询平台属性信息
        List<PlatformPropertyKey> platformPropertyList = productFeignClient.getPlatformPropertyBySkuId(skuId);
        if (!CollectionUtils.isEmpty(platformPropertyList)) {
            List<SearchPlatformProperty> searchPlatformPropertyList = platformPropertyList.stream().map(myPlatformProperty -> {
                SearchPlatformProperty searchPlatformProperty = new SearchPlatformProperty();
                //平台属性Id
                searchPlatformProperty.setPropertyKeyId(myPlatformProperty.getId());
                //平台属性名称
                searchPlatformProperty.setPropertyKey(myPlatformProperty.getPropertyKey());
                //平台属性值
                PlatformPropertyValue platformPropertyValue = myPlatformProperty.getPropertyValueList().get(0);
                searchPlatformProperty.setPropertyValue(platformPropertyValue.getPropertyValue());
                return searchPlatformProperty;
            }).collect(Collectors.toList());

            product.setPlatformProperty(searchPlatformPropertyList);
        }

        //e.存储在es当中
        productMapper.save(product);
    }

    @Override
    public void offSale(Long skuId) {
        productMapper.deleteById(skuId);
    }

    @Override
    public void incrHotScore(Long skuId) {
        //对哪个键进行操作
        String hotKey = "sku:hotScore";
        Double count = redisTemplate.opsForZSet().incrementScore(hotKey, skuId, 1);
        //到达一定次数以后再操作es
        if (count % 6 == 0) {
            Optional<Product> optional = productMapper.findById(skuId);
            Product esProduct = optional.get();
            esProduct.setHotScore(Math.round(count));
            productMapper.save(esProduct);
        }
    }

    @Override
    @SneakyThrows
    public SearchResponseVo searchProduct(SearchParam searchParam) {
        //a.生成DSL查询语句
        SearchRequest searchRequest = buildQueryDsl(searchParam);
        //b.通过DSL语句实现查询
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        //c.对查询结果进行解析并封装到对象当中
        SearchResponseVo searchResponseVo = parseSearchResult(searchResponse);
        //d.返回其他参数
        searchResponseVo.setPageNo(searchParam.getPageNo());
        searchResponseVo.setPageSize(searchParam.getPageSize());
        //e.设置总页数
        boolean flag = searchResponseVo.getTotal() % searchResponseVo.getPageSize() == 0;
        long totalPage = 0L;
        if (flag) {
            totalPage = searchResponseVo.getTotal() / searchResponseVo.getPageSize();
        } else {
            totalPage = searchResponseVo.getTotal() / searchResponseVo.getPageSize() + 1;
        }
        searchResponseVo.setTotalPages(totalPage);
        return searchResponseVo;
    }

    //c.对查询结果进行解析并封装到对象当中
    private SearchResponseVo parseSearchResult(SearchResponse searchResponse) {
        SearchResponseVo searchResponseVo = new SearchResponseVo();

        //1.拿到商品的基本信息
        SearchHits firstHits = searchResponse.getHits();
        //设置总个数
        searchResponseVo.setTotal(firstHits.totalHits);
        SearchHit[] secondHits = firstHits.getHits();
        if (secondHits != null && secondHits.length > 0) {
            for (SearchHit secondHit : secondHits) {
                Product product = JSONObject.parseObject(secondHit.getSourceAsString(), Product.class);
                //拿到高亮信息
                HighlightField highlightField = secondHit.getHighlightFields().get("productName");
                if (highlightField != null) {
                    Text fragment = highlightField.getFragments()[0];
                    product.setProductName(fragment.toString());
                }
                searchResponseVo.getProductList().add(product);
            }
        }

        //2.拿到商品的品牌信息
        ParsedLongTerms brandIdAgg = searchResponse.getAggregations().get("brandIdAgg");
        List<SearchBrandVo> searchBrandVoList = brandIdAgg.getBuckets().stream().map(bucket -> {
            SearchBrandVo searchBrandVo = new SearchBrandVo();
            //品牌的id
            String brandId = bucket.getKeyAsString();
            searchBrandVo.setBrandId(Long.parseLong(brandId));
            //品牌的name
            ParsedStringTerms brandNameAgg = bucket.getAggregations().get("brandNameAgg");
            String brandName = brandNameAgg.getBuckets().get(0).getKeyAsString();
            searchBrandVo.setBrandName(brandName);
            //品牌的url
            ParsedStringTerms brandLogoUrlAgg = bucket.getAggregations().get("brandLogoUrlAgg");
            String brandLogoUrl = brandLogoUrlAgg.getBuckets().get(0).getKeyAsString();
            searchBrandVo.setBrandLogoUrl(brandLogoUrl);
            return searchBrandVo;
        }).collect(Collectors.toList());
        searchResponseVo.setBrandVoList(searchBrandVoList);

        //3.拿到商品的平台属性
        ParsedNested platformPropertyAgg = searchResponse.getAggregations().get("platformPropertyAgg");
        //拿到嵌套的子agg
        ParsedLongTerms propertyKeyIdAgg = platformPropertyAgg.getAggregations().get("propertyKeyIdAgg");
        List<SearchPlatformPropertyVo> searchPlatformPropertyVoList = propertyKeyIdAgg.getBuckets().stream().map(bucket -> {
            SearchPlatformPropertyVo searchPlatformPropertyVo = new SearchPlatformPropertyVo();
            //平台属性id
            Number propertyKeyId = bucket.getKeyAsNumber();
            searchPlatformPropertyVo.setPropertyKeyId(propertyKeyId.longValue());
            //当前属性值的集合
            ParsedStringTerms propertyKeyAgg = bucket.getAggregations().get("propertyKeyAgg");
            String propertyKey = propertyKeyAgg.getBuckets().get(0).getKeyAsString();
            searchPlatformPropertyVo.setPropertyKey(propertyKey);
            //平台属性名称
            ParsedStringTerms propertyValueAgg = bucket.getAggregations().get("propertyValueAgg");
//            List<String> propertyValueList = propertyValueAgg.getBuckets().stream().map(bucket2 -> {
//                return bucket2.getKeyAsString();
//            }).collect(Collectors.toList());
            List<String> propertyValueList = propertyValueAgg.getBuckets().stream().map(Terms.Bucket::getKeyAsString).collect(Collectors.toList());
            searchPlatformPropertyVo.setPropertyValueList(propertyValueList);
            return searchPlatformPropertyVo;
        }).collect(Collectors.toList());
        searchResponseVo.setPlatformPropertyList(searchPlatformPropertyVoList);

        return searchResponseVo;
    }

    //a.生成DSL查询语句
    private SearchRequest buildQueryDsl(SearchParam searchParam) {
        //1.构造一个query
        SearchSourceBuilder esSqlBuilder = new SearchSourceBuilder();

        //2.构造第一个bool
        BoolQueryBuilder firstBool = QueryBuilders.boolQuery();

        //3.构造分类过滤器
        if (!StringUtils.isEmpty(searchParam.getCategory1Id())) {
            //构造一级分类过滤器
            TermQueryBuilder category1Id = QueryBuilders.termQuery("category1Id", searchParam.getCategory1Id());
            firstBool.filter(category1Id);
        }
        if (!StringUtils.isEmpty(searchParam.getCategory2Id())) {
            //构造二级分类过滤器
            TermQueryBuilder category2Id = QueryBuilders.termQuery("category2Id", searchParam.getCategory2Id());
            firstBool.filter(category2Id);
        }
        if (!StringUtils.isEmpty(searchParam.getCategory3Id())) {
            //构造三三级分类过滤器
            TermQueryBuilder category3Id = QueryBuilders.termQuery("category3Id", searchParam.getCategory3Id());
            firstBool.filter(category3Id);
        }

        //4.构造品牌过滤器 &brandName=1:苹果
        String brandName = searchParam.getBrandName();
        if (!StringUtils.isEmpty(brandName)) {
            String[] brandParam = brandName.split(":");
            if (brandParam.length == 2) {
                firstBool.filter(QueryBuilders.termQuery("brandId", brandParam[0]));
            }
        }

        //5.构造商品名称搜索关键字查询 参数格式 ?keyword=手机
        String keyword = searchParam.getKeyword();
        if (!StringUtils.isEmpty(keyword)) {
            MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("productName", keyword).operator(Operator.AND);
            firstBool.must(matchQuery);
        }

        //6.构造平台属性过滤器 参数格式: &props=23:8G:运行内存&props=111:骁龙845:CPU型号
        String[] props = searchParam.getProps();
        if (props != null && props.length > 0) {
            for (String prop : props) {
                //111:骁龙845:CPU型号
                String[] platformParams = prop.split(":");
                if (platformParams.length == 3) {
                    BoolQueryBuilder secondBool = QueryBuilders.boolQuery();

                    BoolQueryBuilder childBool = QueryBuilders.boolQuery();
                    TermQueryBuilder propertyKeyId = QueryBuilders.termQuery("platformProperty.propertyKeyId", platformParams[0]);
                    TermQueryBuilder propertyValue = QueryBuilders.termQuery("platformProperty.propertyValue", platformParams[1]);
                    childBool.must(propertyKeyId);
                    childBool.must(propertyValue);

                    secondBool.must(QueryBuilders.nestedQuery("platformProperty", childBool, ScoreMode.None));

                    firstBool.filter(secondBool);
                }
            }
        }
        esSqlBuilder.query(firstBool);

        //7.构造分页,排序
        int from = (searchParam.getPageNo() - 1) * searchParam.getPageSize();
        esSqlBuilder.from(from);
        esSqlBuilder.size(searchParam.getPageSize());

        /**
         * 8.构造排序 &order=2:desc
         * 1---综合排序
         * 2---价格
         */
        String pageOrder = searchParam.getOrder();
        if (!StringUtils.isEmpty(pageOrder)) {
            String[] orderParams = pageOrder.split(":");
            if (orderParams.length == 2) {
                String fileName = "";
                switch (orderParams[0]) {
                    case "1":
                        fileName = "hotScore";
                        break;
                    case "2":
                        fileName = "price";
                        break;
                }
                esSqlBuilder.sort(fileName, "asc".equals(orderParams[1]) ? SortOrder.ASC : SortOrder.DESC);
            }
        } else {
            //如果没有传递排序参数 按照综合人气排序
            esSqlBuilder.sort("hotScore", SortOrder.DESC);
        }

        //9.编写高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("productName");
        highlightBuilder.preTags("<span style=color:red>");
        highlightBuilder.postTags("</span>");
        esSqlBuilder.highlighter(highlightBuilder);

        //10.设置品牌聚合
        TermsAggregationBuilder brandIdAggBuilder = AggregationBuilders.terms("brandIdAgg").field("brandId")
                .subAggregation(AggregationBuilders.terms("brandNameAgg").field("brandName"))
                .subAggregation(AggregationBuilders.terms("brandLogoUrlAgg").field("brandLogoUrl"));

        esSqlBuilder.aggregation(brandIdAggBuilder);

        //11.设置平台属性聚合
        esSqlBuilder.aggregation(AggregationBuilders.nested("platformPropertyAgg", "platformProperty")
                .subAggregation(AggregationBuilders.terms("propertyKeyIdAgg").field("platformProperty.propertyKeyId")
                        .subAggregation(AggregationBuilders.terms("propertyKeyAgg").field("platformProperty.propertyKey"))
                        .subAggregation(AggregationBuilders.terms("propertyValueAgg").field("platformProperty.propertyValue"))
                )
        );

        //12.查询哪个index和type
        SearchRequest searchRequest = new SearchRequest("product");
        searchRequest.types("info");
        searchRequest.source(esSqlBuilder);
        System.out.println("拼接好的DSL语句:" + esSqlBuilder.toString());

        return searchRequest;
    }
}
